/* Matías Blanco & Sergio Pérez      */
/* Aeropuerto. Práctica 2 - Monitores en Java */

package aerojava;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

enum TEstadoPista{Aterrizar, Despegar, Vacia};
enum TEstadoAvion{QuiereAterrizar, QuiereDespegar, OtraCosa}

public class Main {
    final static int MAXAVIONES = 50;
    
    public static void main(String[] args) {
        Avion avion;
        TorreControl torreControl=new TorreControl();
        List lAviones = new ArrayList(); // Lista donde metemos las aviones
        
        System.out.println("---- COMIENZAN A SALIR AVIONES");System.out.println();
        for(int i=0; i < MAXAVIONES; i++){
            avion = new Avion(i,torreControl);
            avion.start();
            lAviones.add(avion);
        }
    }
}
    
class Pista{
    private int id;
    private TEstadoPista estado;
    private int naviones;

    public Pista(int id){
        this.id=id;
        this.estado=TEstadoPista.Vacia;
        naviones=0;
    }

    public TEstadoPista getEstado() {
        return estado;
    }

    public void setEstado(TEstadoPista estado) {
        this.estado = estado;
    }

    public int getNaviones() {
        return naviones;
    }

    public void setNaviones(int naviones) {
        this.naviones = naviones;
    }
}

class TorreControl{
    final int MAXAVIONES = 50;
    Pista pistas[]=new Pista[2];
    int pistasAviones[]=new int[MAXAVIONES];
    TEstadoAvion estAviones[]=new TEstadoAvion[MAXAVIONES];
    int esperandoAterrizar = 0;

    public TorreControl(){
        pistas[0]=new Pista(1);
        pistas[1]=new Pista(2);
    }

    public synchronized int PedirPistaAterrizar(int idAvion){
        pistasAviones[idAvion]=0;
        estAviones[idAvion]=TEstadoAvion.QuiereAterrizar;
        //Miramos las pistas para ver si hay alguna en la que se pueda aterrizar
        for(int i=0; i<2; i++){
            if ((pistas[i].getEstado() == TEstadoPista.Vacia) && (estAviones[idAvion] == TEstadoAvion.QuiereAterrizar)){
                pistas[i].setEstado(TEstadoPista.Aterrizar);
                pistas[i].setNaviones(pistas[i].getNaviones()+1);
                estAviones[idAvion] = TEstadoAvion.OtraCosa;
                pistasAviones[idAvion]=i+1;
            }
        }
        if (estAviones[idAvion] == TEstadoAvion.QuiereAterrizar) {
            esperandoAterrizar = esperandoAterrizar + 1;
        }
        return pistasAviones[idAvion];
    }

    public synchronized int PuedeAterrizar(int idAvion){
        while(estAviones[idAvion]==TEstadoAvion.QuiereAterrizar){
            try {
                this.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return pistasAviones[idAvion];
    }

    public synchronized int PedirPistaDespegar(int idAvion){
        pistasAviones[idAvion]=0;
        estAviones[idAvion]=TEstadoAvion.QuiereDespegar;
        // Solo se puede despegar cuando no haya ninguna avion esperando Aterrizar
        if (esperandoAterrizar==0){
            // Miramos las pistas para ver si hay alguna en la que se pueda despegar
            for(int i=0; i<2; i++){
                if (((pistas[i].getEstado() == TEstadoPista.Vacia) || (pistas[i].getEstado() == TEstadoPista.Despegar && pistas[i].getNaviones()<3)) && (estAviones[idAvion] == TEstadoAvion.QuiereDespegar)){
                    pistas[i].setEstado(TEstadoPista.Despegar);
                    pistas[i].setNaviones(pistas[i].getNaviones()+1);
                    estAviones[idAvion] = TEstadoAvion.OtraCosa;
                    pistasAviones[idAvion]=i+1;
                }
            }
        }
        return pistasAviones[idAvion];
    }

    public synchronized int PuedeDespegar(int idAvion){
        while(estAviones[idAvion]==TEstadoAvion.QuiereDespegar){
            try {
                this.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return pistasAviones[idAvion];
    }

    public synchronized void DejarPista(int idPista){
        // Liberamos de una avión la pista indicada
        pistas[idPista].setNaviones(pistas[idPista].getNaviones() - 1);
        if (pistas[idPista].getNaviones() == 0)
            pistas[idPista].setEstado(TEstadoPista.Vacia);
        GestionarEsperas();
    }

    // Definimos el procedimiento para poder gestionar las aviones que esten esperando
    private void GestionarEsperas(){
        for(int av=0; av<MAXAVIONES; av++){
            if(estAviones[av]==TEstadoAvion.QuiereAterrizar){
                for(int i=0; i<2; i++){
                    if ((pistas[i].getEstado() == TEstadoPista.Vacia) && (estAviones[av] == TEstadoAvion.QuiereAterrizar)){
                        pistas[i].setEstado(TEstadoPista.Aterrizar);
                        pistas[i].setNaviones(pistas[i].getNaviones()+1);
                        estAviones[av] = TEstadoAvion.OtraCosa;
                        pistasAviones[av]=i+1;
                        esperandoAterrizar = esperandoAterrizar - 1;
                        // Avisamos a los que estan esperando
                        this.notifyAll();
                    }
                }
            }else if(estAviones[av]==TEstadoAvion.QuiereDespegar && esperandoAterrizar == 0){
                for(int i=0; i<2; i++){
                    if (((pistas[i].getEstado() == TEstadoPista.Vacia) || (pistas[i].getEstado() == TEstadoPista.Despegar && pistas[i].getNaviones()<3)) && (estAviones[av] == TEstadoAvion.QuiereDespegar)){
                        pistas[i].setEstado(TEstadoPista.Despegar);
                        pistas[i].setNaviones(pistas[i].getNaviones()+1);
                        estAviones[av] = TEstadoAvion.OtraCosa;
                        pistasAviones[av]=i+1;
                        // Avisamos a los que estan esperando
                        this.notifyAll();
                    }
                }
            }
        }
    }
}

class Avion extends Thread{
    private int id;
    private int pista;
    private TorreControl control;

    public Avion(int idAvion, TorreControl tc){
        id=idAvion;
        control=tc;
    }

    @Override
    public void run(){

        try {
            // Despegar
            System.out.println("-- El avion " + id + " quiere despegar.");
            pista = control.PedirPistaDespegar(id);
            while (pista == 0) {
                pista = control.PuedeDespegar(id);
            }
            System.out.println("++> El avion " + id + " esta despegando en la pista " + pista);
            sleep(1000); // Tiempo que tarda en despegar
            System.out.println("--> El avion " + id + " ha despegado en la pista " + pista);
            control.DejarPista(pista-1);

            sleep(3000); // Tiempo que esta volando
            // Aterrizar
            System.out.println("-- El avion " + id + " quiere aterrizar.");
            pista = control.PedirPistaAterrizar(id);
            while (pista == 0) {
                pista = control.PuedeAterrizar(id);
            }
            System.out.println("<++ El avion " + id + " esta aterrizando en la pista ");
            sleep(1000); // Tiempo que tarda en aterrizar
            System.out.println("<-- El avion " + id + " ha aterrizado en la pista " + pista);
            control.DejarPista(pista-1);
        } catch (InterruptedException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }        

}
